package com.easylinkin.linkappapi.facerecognition.service.oss.impl;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyuncs.utils.StringUtils;
import com.easylinkin.linkappapi.facerecognition.dto.OSSPolicy;
import com.easylinkin.linkappapi.facerecognition.service.oss.ObjectStorageOutsideService;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import org.apache.http.util.CharsetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

public class AliObjectStorageService implements ObjectStorageOutsideService {

    private static final Logger log = LoggerFactory.getLogger(AliObjectStorageService.class);

    private final OSSClient aliClient;

    public AliObjectStorageService(OSSClient aliClient) {
        this.aliClient = aliClient;
    }

    @Override
    public void createBucket(String bucketName) {
        try {
            boolean found = checkBucketExist(bucketName);

            if (!found) {
                // 创建存储空间
                aliClient.createBucket(bucketName);
                log.info("Create Ali oss bucket " + bucketName);
            }
        } catch (OSSException | ClientException e) {
            log.error("Create Ali oss bucket error : " + bucketName, e);
        }
    }

    @Override
    public boolean checkBucketExist(String bucketName) {

        try {
            return aliClient.doesBucketExist(bucketName);
        } catch (OSSException | ClientException e) {
            log.error("Check Ali oss bucket exist error : " + bucketName, e);
        }
        return false;
    }

    @Override
    public void createFolder(String bucketName, String folder) {

        try {
            boolean found = checkObjectExist(bucketName, folder);

            if (!found) {
                aliClient.putObject(bucketName, folder, new ByteArrayInputStream(new byte[0]));
            }
        } catch (OSSException | ClientException e) {
            log.error("Create folder Ali oss error " + folder, e);
        }
    }

    @Override
    public boolean checkObjectExist(String bucketName, String objectName) {
        try {
            return aliClient.doesObjectExist(bucketName, objectName);
        } catch (OSSException | ClientException e) {
            log.error("Check Ali oss object exist error : " + objectName, e);
        }

        return false;
    }

    @Override
    public void deleteFile(String bucketName, String objectName) {

        try {
            aliClient.deleteObject(bucketName, objectName);
        } catch (OSSException | ClientException e) {
            log.error("Delete file Ali oss error " + objectName, e);
        }
    }

    @Override
    public String generatePresignedUrl(String bucketName, String objectName, int expiry) {

        try {
            // 设置URL的过期时间，将秒转换为毫秒数
            Date expiration = new Date(System.currentTimeMillis() + expiry * 1000);
            // 生成URL
            URL url = aliClient.generatePresignedUrl(bucketName, objectName, expiration);
            if (url != null) {
                return url.toString();
            }
        } catch (ClientException e) {
            log.error("Generate url Ali oss error " + objectName , e);
        }
        return null;
    }

    @Override
    public void uploadImgObject(String bucketName, String file, InputStream is) {

        try {
            // 创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            // 上传的文件的长度
            metadata.setContentLength(is.available());
            // 指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            // 指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            // 指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            // 文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            // 如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType("image/jpeg");
            /*// 指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + file
                    + "/" + fileSize + "Byte.");*/
            // 上传文件 (上传文件流的形式)
            aliClient.putObject(bucketName, file, is, metadata);

        } catch (IOException e) {
            log.error("Upload object Ali oss error " + file, e);
        }
    }

    @Override
    public InputStream getObjectInputStream(String bucketName, String objectName) {

        try {
            return aliClient.getObject(bucketName, objectName).getObjectContent();
        } catch (OSSException | ClientException e) {
            log.error("Get Object InputStream Ali oss error " + objectName, e);
        }

        return null;
    }

    @Override
    public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName) {
        try {
            aliClient.copyObject(bucketName, objectName, destBucketName, destObjectName);
        } catch (OSSException | ClientException e) {
            log.error("Copy Object Ali oss error " + objectName, e);
        }
    }

    @Override
    public void putObject(String bucketName, String objectName, InputStream in) {
        try {
            aliClient.putObject(bucketName, objectName, in);
        } catch (OSSException | ClientException e) {
            log.error("Put Object Ali oss error " + objectName, e);
        }
    }

  @Override
  public void putObject(String bucketName, String objectName, InputStream in, String contentType) {
    try {
      aliClient.putObject(bucketName, objectName, in);
    } catch (OSSException | ClientException e) {
      log.error("Put Object Ali oss error " + objectName, e);
    }
  }

  @Value("${oss.bucket}")
    private String bucket;
    @Value("${oss.endPoint-public}")
    private String endPointPublic;
    @Value("${oss.readHost}")
    private String readHost;
    @Value("${oss.accessKeyId}")
    private String accessKeyId;
    @Override
    public OSSPolicy generatePostPolicy(String dir) {
      String uploadHost = "http://" + this.bucket + "." + this.endPointPublic; // host的格式为 bucketname.endpoint
      String readHost = StringUtils.isEmpty(this.readHost) ? uploadHost : this.readHost;
      if (!readHost.startsWith("http:") && !readHost.startsWith("https:")) {
        readHost = "http://" + readHost;
      }
      String accessHost = readHost;
      
      long expireTime = 30 * 60;
      long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
      Date expiration = new Date(expireEndTime);
      PolicyConditions policyConds = new PolicyConditions();
      policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
      policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
      
      String postPolicy = this.generatePostPolicy(expiration, policyConds);
      byte[] binaryData = postPolicy.getBytes(CharsetUtils.lookup("utf-8"));
      String encodedPolicy = BinaryUtil.toBase64String(binaryData);
      String postSignature = this.calculatePostSignature(postPolicy);
      
      OSSPolicy p = new OSSPolicy();
      p.setAccessId(accessKeyId);
      p.setPolicy(encodedPolicy);
      p.setSignature(postSignature);
      p.setUploadHost(uploadHost);
      p.setAccessHost(accessHost);
      p.setPath(dir);
      return p;
    }


  public String generatePostPolicy(Date expiration, PolicyConditions policyConditions) {
      String postPolicy = "";
      try {
        postPolicy = this.aliClient.generatePostPolicy(expiration, policyConditions);
      } catch (Exception e) {
        log.error(e.getMessage(), e);
      }
      return postPolicy;
    }
    
    public String calculatePostSignature(String postPolicy) {
      return this.aliClient.calculatePostSignature(postPolicy);
    }

  @Override
  public void uploadFile(String bucket, MultipartFile file, String fileName) {
    try {
      aliClient.putObject(bucket, fileName, file.getInputStream());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
